from flask import Flask, request, jsonify    # 导入 Flask 及相关模块
from flask_sqlalchemy import SQLAlchemy      # 导入 SQLAlchemy ORM
from flask_cors import CORS
from datetime import datetime                  # 用于时间字段
import os

# 初始化 Flask 应用
app = Flask(__name__)
CORS(app)  # 启用跨域支持

# 配置数据库
basedir = os.path.abspath(os.path.dirname(__file__))
# app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'todolist.db')
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///todolist.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False  # 禁用跟踪修改

db = SQLAlchemy(app)       # 初始化 ORM

# 待办事项数据模型
class Todo(db.Model):
    __tablename__ = 'todos'
    
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100), nullable=False)  # 标题不能为空
    description = db.Column(db.String(200))
    completed = db.Column(db.Boolean, default=False)  # 默认未完成
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)  # 自动更新
    
    def to_dict(self):
        """将模型转换为字典格式"""
        return {
            'id': self.id,
            'title': self.title,
            'description': self.description,
            'completed': self.completed,
            'created_at': self.created_at.isoformat(),
            'updated_at': self.updated_at.isoformat()
        }

# 创建数据库表
with app.app_context():
    db.create_all()

# 错误处理
@app.errorhandler(404)
def not_found(error):
    return jsonify({'error': 'Resource not found'}), 404

@app.errorhandler(400)
def bad_request(error):
    return jsonify({'error': 'Bad request'}), 400

@app.errorhandler(500)
def internal_error(error):
    return jsonify({'error': 'Internal server error'}), 500

# 辅助函数 - 验证待办事项数据
def validate_todo_data(data, is_update=False):
    errors = []
    
    # 创建时需要验证所有字段，更新时只验证提供的字段
    if not is_update or 'title' in data:
        if not data.get('title') or len(data.get('title', '').strip()) == 0:
            errors.append('Title is required')
        elif len(data['title'].strip()) > 100:
            errors.append('Title must be less than 100 characters')
    
    if not is_update or 'description' in data:
        if data.get('description') and len(data['description']) > 200:
            errors.append('Description must be less than 200 characters')
    
    if not is_update or 'completed' in data:
        if 'completed' in data and not isinstance(data['completed'], bool):
            errors.append('Completed must be a boolean value')
    
    return errors


# RESTful API 接口

# 1. 获取所有待办事项
@app.route('/api/todos', methods=['GET'])
def get_todos():
    try:
        todos = Todo.query.order_by(Todo.updated_at.desc()).all()  # 按更新时间倒序
        return jsonify([todo.to_dict() for todo in todos])
    except Exception as e:
        app.logger.error(f'Error fetching todos: {str(e)}')
        return jsonify({'error': 'Failed to fetch todos'}), 500

# 2. 创建新待办事项
@app.route('/api/todos', methods=['POST'])
def create_todo():
    data = request.get_json()

# 验证数据
    errors = validate_todo_data(data)
    if errors:
        return jsonify({'errors': errors}), 400
    
    try:
        todo = Todo(
            title=data['title'].strip(),
            description=data.get('description', '').strip(),
            completed=data.get('completed', False)
        )
        db.session.add(todo)
        db.session.commit()
        return jsonify(todo.to_dict()), 201
    except Exception as e:
        db.session.rollback()
        app.logger.error(f'Error creating todo: {str(e)}')
        return jsonify({'error': 'Failed to create todo'}), 500

# 3. 获取单个待办事项
@app.route('/api/todos/<int:todo_id>', methods=['GET'])
def get_todo(todo_id):
    try:
        todo = Todo.query.get_or_404(todo_id)
        return jsonify(todo.to_dict())
    except Exception as e:
        app.logger.error(f'Error fetching todo {todo_id}: {str(e)}')
        return jsonify({'error': 'Failed to fetch todo'}), 500

# 4. 更新待办事项
@app.route('/api/todos/<int:todo_id>', methods=['PUT'])
def update_todo(todo_id):
    data = request.get_json()
    
    # 验证数据
    errors = validate_todo_data(data, is_update=True)
    if errors:
        return jsonify({'errors': errors}), 400
    
    try:
        todo = Todo.query.get_or_404(todo_id)
        
        if 'title' in data:
            todo.title = data['title'].strip()
        if 'description' in data:
            todo.description = data['description'].strip()
        if 'completed' in data:
            todo.completed = data['completed']
        
        db.session.commit()
        return jsonify(todo.to_dict())
    except Exception as e:
        db.session.rollback()
        app.logger.error(f'Error updating todo {todo_id}: {str(e)}')
        return jsonify({'error': 'Failed to update todo'}), 500

# 5. 删除待办事项
@app.route('/api/todos/<int:todo_id>', methods=['DELETE'])
def delete_todo(todo_id):
    try:
        todo = Todo.query.get_or_404(todo_id)
        db.session.delete(todo)
        db.session.commit()
        return '', 204
    except Exception as e:
        db.session.rollback()
        app.logger.error(f'Error deleting todo {todo_id}: {str(e)}')
        return jsonify({'error': 'Failed to delete todo'}), 500

# 6. 切换完成状态
@app.route('/api/todos/<int:todo_id>/toggle', methods=['PATCH'])
def toggle_todo(todo_id):
    try:
        todo = Todo.query.get_or_404(todo_id)
        todo.completed = not todo.completed
        db.session.commit()
        return jsonify(todo.to_dict())
    except Exception as e:
        db.session.rollback()
        app.logger.error(f'Error toggling todo {todo_id}: {str(e)}')
        return jsonify({'error': 'Failed to toggle todo status'}), 500

 
# 启动 Flask 服务，开启调试模式
if __name__ == '__main__':
    app.run(port=1555, debug=True)